package com.lagou.bean;

import lombok.Data;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Data
@Component
public class MonitorManager {

    private Map<String,List<BeanMonitor>> beanMonitorMap = new ConcurrentHashMap<>();

    private static final MonitorManager INSTANCE = new MonitorManager();

    public static MonitorManager getInstance(){
        return INSTANCE;
    }

    /**
     * 添加bean统计信息
     * @param beanMonitor
     */
    public void addBeanMonitor(BeanMonitor beanMonitor){
        String methodReferenceName = beanMonitor.getMethodReferenceName();
        List<BeanMonitor> beanMonitors;
        if (beanMonitorMap.containsKey(methodReferenceName)) {
            beanMonitors = beanMonitorMap.get(methodReferenceName);
        } else {
            beanMonitors = new CopyOnWriteArrayList<>();
            beanMonitorMap.put(methodReferenceName, beanMonitors);
        }
        beanMonitors.add(beanMonitor);
    }

    /**
     * 统计tp90
     */
    public void statisticsTp90(){
        for (Map.Entry<String, List<BeanMonitor>> entry : beanMonitorMap.entrySet()) {
            String methodReferenceName = entry.getKey();
            List<BeanMonitor> beanMonitors = entry.getValue();
            if(beanMonitors == null || beanMonitors.size() == 0){
                return;
            }
            Collections.sort(beanMonitors);
            System.out.println("max time:" + beanMonitors.get(beanMonitors.size()-1).getInvokeTime() + " min time:" + beanMonitors.get(0).getInvokeTime());
            int index = (int)Math.ceil((beanMonitors.size() - 1) * 0.9);
            BeanMonitor beanMonitor = beanMonitors.get(index);
            System.out.println("TP90 " + methodReferenceName + " invoke time:" + beanMonitor.getInvokeTime());
        }
    }

    /**
     * 统计tp99
     */
    public void statisticsTp99(){
        for (Map.Entry<String, List<BeanMonitor>> entry : beanMonitorMap.entrySet()) {
            String methodReferenceName = entry.getKey();
            List<BeanMonitor> beanMonitors = entry.getValue();
            if(beanMonitors == null || beanMonitors.size() == 0){
                return;
            }
            Collections.sort(beanMonitors);
            System.out.println("max time:" + beanMonitors.get(beanMonitors.size()-1).getInvokeTime() + " min time:" + beanMonitors.get(0).getInvokeTime());
            int index = (int)Math.ceil((beanMonitors.size() - 1) * 0.99);
            BeanMonitor beanMonitor = beanMonitors.get(index);
            System.out.println("TP99 " + methodReferenceName + " invoke time:" + beanMonitor.getInvokeTime());
        }
    }

    /**
     * 清理超过1分钟的数据
     */
    public void cleanOver1Min(){
        beanMonitorMap.entrySet().stream().forEach(entry -> {
            String methodReferenceName = entry.getKey();
            List<BeanMonitor> beanMonitors = entry.getValue();
            if (beanMonitors == null || beanMonitors.size() == 0) {
                return;
            }
            List<BeanMonitor> beanMonitorList = beanMonitors.stream()
                    .filter(beanMonitor -> calLastedTime(beanMonitor.getStatisticsDate()) > 60)
                    .collect(Collectors.toList());
            if (beanMonitorList != null && beanMonitorList.size() > 0) {
                beanMonitors.removeAll(beanMonitorList);
                System.out.println("clean overOneMinSize:" + beanMonitorList.size());
            }
        });
    }

    @Scheduled(fixedDelay = 5000)
    public void statistics(){
        MonitorManager.getInstance().cleanOver1Min();
        MonitorManager.getInstance().statisticsTp90();
        MonitorManager.getInstance().statisticsTp99();
    }

    public int calLastedTime(Date startDate) {
        long now = new Date().getTime();
        long before = startDate.getTime();
        int c = (int)((now - before) / 1000);
//        int c = (int)((now - before));
        return c;
    }

}
